/*
 * 版权所有 (C) 2015 知启蒙(ZHIQIM) 保留所有权利。[遇见知启蒙，邂逅框架梦，本文采用木兰宽松许可证第2版]
 * 
 * https://zhiqim.org/project/zhiqim_products/zhiqim_tunneler.htm
 *
 * Zhiqim Tunneler is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *          http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */
package org.zhiqim.tunnel;

import java.io.EOFException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.SocketException;
import java.net.SocketTimeoutException;

import org.zhiqim.kernel.constants.SignConstants;
import org.zhiqim.kernel.control.Threadx;
import org.zhiqim.kernel.logging.Log;
import org.zhiqim.kernel.logging.LogFactory;
import org.zhiqim.kernel.util.Ids;
import org.zhiqim.kernel.util.Streams;

/**
 * 隧道连接抽象类
 *
 * @version v1.5.0 @author zouzhigang 2018-11-9 新建与整理
 */
public abstract class TunnelConnection extends Threadx implements SignConstants
{
    protected static final Log log = LogFactory.getLog(TunnelServer.class);
    
    //连接名称和编号
    protected final String connName;
    protected final long connId;
    
    //输入输出流
    protected final Socket socket;
    protected final InputStream input;
    protected final OutputStream output;
    
    public TunnelConnection(String connName, long connId, Socket socket, InputStream input, OutputStream output)
    {
        this.connName = connName;
        this.connId = connId;
        
        this.socket = socket;
        this.input = input;
        this.output = output;
    }
    
    /**
     * 网络连接发送消息对象
     * 
     * @param in        对端的输入流
     */
    public void send(InputStream in) throws Exception
    {
        Streams.putBytes(in, output);
    }
    
    /**
     * 网络连接接收消息对象
     * 
     * @param in        对端的输入流
     */
    public abstract void receive(InputStream in) throws Exception;
    
    /**
     * 网络连接接收对端主动退出，如：
     * 
     * 1、收到对端主动退出协议包
     * 2、收到对端EOFException
     */
    public abstract void exit();
    
    /**
     * 网络连接异常处理
     * 
     * @param e         异常对象
     */
    public abstract void exception(Throwable e);
    
    /**********************************************************************************/
    //连接编号&时间属性
    /**********************************************************************************/
    
    public long getConnId()
    {
        return connId;
    }
    
    public String getConnTimeString()
    {
        return Ids.toDateTime(connId);
    }
    
    @Override /** 线程名 */
    protected String getThreadName()
    {
        return connName + "-" + connId;
    }
    
    /**********************************************************************************/
    //链路检测请求
    /**********************************************************************************/
    
    @Override /** 线程持续运行 */
    protected void loop()
    {
        try
        {
            receive(input);
        }
        catch (SocketTimeoutException e)
        {//超时继续读取
            return;
        }
        catch (EOFException e)
        {//读流时对端关闭
            log.info(connName+"收到对端主动关闭连接");
            exit();
        }
        catch (SocketException e)
        {//读流时Socket异常
            if (_SOCKET_CLOSED_.equalsIgnoreCase(e.getMessage()))
            {//本身socket被业务层关闭
                exception(e);
            }
            else
            {//对端
                log.info(connName+"收到对端异常关闭连接");
                exit();
            }
        }
        catch (Throwable e)
        {
            log.info(connName+"收到对端连接异常", e);
            exception(e);
        }
    }
}
